package com.fowo.api.common.model;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 分页搜索
 */
@Data
public class PageSearch {
    public static final Pattern ASC_PATTERN = Pattern.compile("\\basc\\b", Pattern.CASE_INSENSITIVE);
    public static final Pattern DESC_PATTERN = Pattern.compile("\\bdesc\\b", Pattern.CASE_INSENSITIVE);

    /**
     * 获取关联字段的查询表别名信息
     */
    public Map<String, String> getJoinFieldTableAliasMap() {
        return Collections.emptyMap();
    }

    /**
     * 获取可选关联字段的信息
     */
    public Map<String, OptionalJoinField> getOptionalJoinFields() {
        return Collections.emptyMap();
    }

    /**
     * 表示查询因为关联需要添加 group by
     */
    @JsonIgnore
    @Schema(hidden = true)
    private boolean needGroupBy;

    /**
     * 每页记录数
     */
    @Schema(title = "每页记录数")
    private Integer pageSize = 20;

    /**
     * 当前页号
     */
    @Schema(title = "当前页号")
    private Integer current = 1;

    /**
     * ProTable 样式的 Sort JSON 格式数据
     */
    @Schema(title = "ProTable 样式的 Sort JSON 格式数据")
    private String sort;

    /**
     * 关键字搜索
     */
    @Schema(title = "关键字搜索")
    private String keyword;

    @JsonIgnore
    @Schema(hidden = true)
    private String orderBy;

    /**
     * 限制查询字段
     */
    @JsonIgnore
    @Schema(hidden = true)
    private List<String> selectFields;

    /**
     * 主列表包括子列表时，限制要使用的分段
     */
    @JsonIgnore
    @Schema(hidden = true)
    private List<String> fillParts;

    @Schema(title = "限制查询主键列表")
    private List<Long> selectedIds;

    /**
     * 限制最大返回记录数
     */
    @Schema(title = "最大返回记录数")
    private Integer limit;

    /**
     * 自定义过滤器
     */
    @Schema(title = "自定义过滤器")
    private String filters;

    @JsonIgnore
    @Schema(hidden = true)
    private List<PageSearchFilter> finalFilters;

    @Schema(title = "接收高级搜索条件参数")
    private String superQueryParams;

    @Schema(title = "接收高级搜索类型：and, or")
    private String superQueryMatchType;

    /**
     * 自定义多列排序
     */
    @Schema(title = "自定义多列排序")
    private String sorts;

    @JsonIgnore
    @Schema(hidden = true)
    private List<PageSearchSort> finalSorts;

    @JsonIgnore
    @Schema(hidden = true)
    private Long currentUserId;

    /**
     * 自定义扩展数据
     */
    @JsonIgnore
    @Schema(hidden = true)
    private final Map<String, Object> extendData = new HashMap<>();

    /**
     * 禁用搜索时用户数据权限过滤
     */
    @JsonIgnore
    @Schema(hidden = true)
    private boolean disableDataPremissions = false;

    /**
     * 输入的条件转换成的 SQL JOIN 语句段
     */
    @JsonIgnore
    @Schema(hidden = true)
    private String customSqlJoinSegments;

    /**
     * 输入的条件转换成的 SQL WHERE 语句段
     */
    @JsonIgnore
    @Schema(hidden = true)
    private String customSqlWhereSegments;

    /**
     * 获取 SQL JOIN 语句段，如果未初始化，将自动调用一次 applySqlSegments
     */
    public String getCustomSqlJoinSegments() {
        if (customSqlJoinSegments == null) {
            applySqlSegments();
        }
        return customSqlJoinSegments;
    }

    /**
     * 获取 SQL WHERE 语句段，如果未初始化，将自动调用一次 applySqlSegments
     */
    public String getCustomSqlWhereSegments() {
        if (customSqlWhereSegments == null) {
            applySqlSegments();
        }
        return customSqlWhereSegments;
    }

    /**
     * 应用搜索条件到 customSqlWhereSegments
     */
    public void applySqlSegments() {
    }

    /**
     * 使用字符串 filters, sorts 初始化 finalFilters, filterSorts;
     */
    public void initFinal() {
        ObjectMapper mapper = new ObjectMapper();
        if (StringUtils.hasText(filters)) {
            try {
                List<PageSearchFilter> finalFilters = mapper.readValue(filters, new TypeReference<List<PageSearchFilter>>() {
                });
                this.finalFilters = finalFilters;
            } catch (JsonProcessingException e) {
                // 忽略此错误
            }
        } else if (StringUtils.hasText(superQueryParams)) {
            if (superQueryParams.toUpperCase().startsWith("%5B") && superQueryParams.toUpperCase().endsWith("%5D")) {
                // 反向解析通过 URL 编译过的参数
                superQueryParams = URLDecoder.decode(superQueryParams, StandardCharsets.UTF_8);
                try {
                    List<PageSearchFilter> finalFilters = new ArrayList<>();
                    // 尝试转换为 PageSearchFilter 格式
                    final List<JSONObject> jsonObjects = JSONArray.parseArray(superQueryParams, JSONObject.class);
                    for (JSONObject jsonObject : jsonObjects) {
                        PageSearchFilter searchFilter = new PageSearchFilter();
                        searchFilter.setDataIndex(new DataIndex(jsonObject.getString("field")));
                        searchFilter.setOp(jsonObject.getString("rule"));
                        searchFilter.setValue(jsonObject.getString("val"));
                        if ("in".equals(searchFilter.getOp()) || "range".equals(searchFilter.getOp())) {
                            if (StringUtils.hasText(searchFilter.getValue())) {
                                searchFilter.setValues(Arrays.asList(searchFilter.getValue().split(",").clone()));
                            } else {
                                // 没有给出搜索值，放置一个空字符串
                                searchFilter.setValues(Collections.singletonList(""));
                            }
                        }
                        finalFilters.add(searchFilter);
                    }
                    this.finalFilters = finalFilters;
                } catch (Exception ignored) {
                }
            }
        }
        if (StringUtils.hasText(sorts)) {
            try {
                if (sorts.startsWith("%5B")) {
                    // 反 url 解码
                    sorts = URLDecoder.decode(sorts, StandardCharsets.UTF_8);
                }
                List<PageSearchSort> finalSorts = mapper.readValue(sorts, new TypeReference<List<PageSearchSort>>() {
                });
                this.finalSorts = finalSorts;
            } catch (JsonProcessingException e) {
                // 忽略此错误
            }
        }
    }

    /**
     * 按 sort 的内容生成 orderBy 值
     *
     * @param defaultOrderBy 默认的 orderBy
     * @param prefix         字段前缀
     */
    public void initSort(String defaultOrderBy, String prefix) {
        initSort(defaultOrderBy, prefix, Collections.emptyMap());
    }

    /**
     * 按 sort 的内容生成 orderBy 值
     *
     * @param defaultOrderBy 默认的 orderBy
     * @param columnMapping  列对应关系
     */
    public void initSort(String defaultOrderBy, Map<String, String> columnMapping) {
        initSort(defaultOrderBy, "", columnMapping);
    }

    /**
     * 按 sort 的内容生成 orderBy 值
     *
     * @param defaultOrderBy 默认的 orderBy
     * @param prefix         字段前缀
     * @param columnMapping  列对应关系
     */
    public void initSort(String defaultOrderBy, String prefix, Map<String, String> columnMapping) {
        initFinal();
        if (finalSorts != null && !finalSorts.isEmpty()) {
            StringBuilder sb = new StringBuilder();
            for (PageSearchSort sort : finalSorts) {
                String column = sort.getDataIndex().toFieldName();
                if (columnMapping.containsKey(column)) {
                    column = columnMapping.get(column);
                } else if (StringUtils.hasText(prefix)) {
                    column = prefix + fieldToColumn(column);
                } else {
                    column = fieldToColumn(column);
                }
                if (sb.length() > 0) {
                    sb.append(", ");
                }
                sb.append(column);
                boolean isAscend = "ascend".equals(sort.getSortOrder());
                sb.append(isAscend ? " asc" : " desc");
            }
            orderBy = sb.toString();
        } else if (StringUtils.hasText(sort)) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                StringBuilder sb = new StringBuilder();
                final Map<String, String> map = objectMapper.readValue(sort, Map.class);
                for (Map.Entry<String, String> kv : map.entrySet()) {
                    String column = kv.getKey();
                    // 中间有点的是子表的排序
                    if (column.contains(".")) {
                        continue;
                    }

                    ColumnMappingAscDesc columnMappingAscDesc = null;
                    if (columnMapping.containsKey(column)) {
                        column = columnMapping.get(column);
                        columnMappingAscDesc = tryFindColumnMappingAscDesc(column);
                    } else if (StringUtils.hasText(prefix)) {
                        column = prefix + fieldToColumn(column);
                    } else {
                        column = fieldToColumn(column);
                    }
                    if (sb.length() > 0) {
                        sb.append(", ");
                    }
                    boolean isAscend = "ascend".equals(kv.getValue());
                    if (columnMappingAscDesc != null) {
                        sb.append(isAscend ? columnMappingAscDesc.ascPart : columnMappingAscDesc.descPart);
                    } else {
                        sb.append(column);
                        sb.append(isAscend ? " asc" : " desc");
                    }
                }
                orderBy = sb.toString();
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        if (!StringUtils.hasText(orderBy)) {
            orderBy = defaultOrderBy;
        }
    }

    /**
     * 返回表示子表 sort 属性的字符串
     * @param childPropName 子表属性名
     */
    public String toChildSort(String childPropName) {
        if (StringUtils.hasText(sort)) {
            String startName = childPropName + ".";
            int startPos = startName.length();
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                StringBuilder sb = new StringBuilder();
                Map<String, String> map = objectMapper.readValue(sort, Map.class);
                Map<String, String> newMap = new HashMap<>();
                for (Map.Entry<String, String> stringStringEntry : map.entrySet()) {
                    if (stringStringEntry.getKey().startsWith(startName)) {
                        newMap.put(stringStringEntry.getKey().substring(startPos), stringStringEntry.getValue());
                    }
                }
                if (!newMap.isEmpty()) {
                    return objectMapper.writeValueAsString(newMap);
                }
            } catch (Exception ignored) {}
        }
        return null;
    }

    private ColumnMappingAscDesc tryFindColumnMappingAscDesc(String orderByPart) {
        boolean anyFind = false;
        ColumnMappingAscDesc columnMappingAscDesc = new ColumnMappingAscDesc();
        columnMappingAscDesc.ascPart = orderByPart;
        columnMappingAscDesc.descPart = orderByPart;
        Matcher ascMatcher = ASC_PATTERN.matcher(columnMappingAscDesc.descPart);
        if (ascMatcher.find()) {
            anyFind = true;
            columnMappingAscDesc.descPart = ascMatcher.replaceAll("desc");
        }
        if (!anyFind) {
            Matcher descMatcher = DESC_PATTERN.matcher(columnMappingAscDesc.descPart);
            if (descMatcher.find()) {
                anyFind = true;
                columnMappingAscDesc.descPart = descMatcher.replaceAll("asc");
            }
        }

        if (anyFind) {
            return columnMappingAscDesc;
        }
        return null;
    }

    static class ColumnMappingAscDesc {
        public String ascPart;
        public String descPart;
    }

    private String fieldToColumn(String column) {
        StringBuilder sb = new StringBuilder();
        for (char c : column.toCharArray()) {
            if (Character.isUpperCase(c)) {
                sb.append("_");
                sb.append(Character.toLowerCase(c));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    public Page toPage() {
        return new Page(current, pageSize);
    }

    public Page toPage(long total) {
        Page p = new Page(current, pageSize);
        p.setSearchCount(false);
        p.setTotal(total);
        return p;
    }

    public Page toOrderPage() {
        Page page = new Page(current, pageSize);
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            final Map<String, String> map = objectMapper.readValue(sort, Map.class);
            for (Map.Entry<String, String> kv : map.entrySet()) {
                String column = kv.getKey();
                boolean isAscend = "ascend".equals(kv.getValue());
                OrderItem item = isAscend ? OrderItem.asc(column) : OrderItem.desc(column);
                page.addOrder(item);
            }
        } finally {
            return page;
        }
    }

    /**
     * 为可选的关联字段提供信息
     */
    @Getter
    @Setter
    @Accessors(chain = true)
    public static class OptionalJoinField implements Serializable {
        /**
         * Sql 中的 Join 段
         */
        private String joinPart;

        /**
         * 关联表别名
         */
        private String tableAlias;

        /**
         * 查询列名
         */
        private String columnName;

        /**
         * 依赖的其它可选关联字段
         */
        private List<String> depends;

        /**
         * 是否需要主查询 group by (主数据的单个记录加入此 join 后会产生重复记录时需要)
         */
        private Boolean needGroupBy;

    }
}
